---
title: "Next.js setup guide"
sidebarTitle: "Next.js"
description: "This guide will show you how to setup Trigger.dev in your existing Next.js project, test an example task, and view the run."
icon: "N"
---

import Prerequisites from "/snippets/framework-prerequisites.mdx";
import CliInitStep from "/snippets/step-cli-init.mdx";
import CliDevStep from "/snippets/step-cli-dev.mdx";
import CliRunTestStep from "/snippets/step-run-test.mdx";
import CliViewRunStep from "/snippets/step-view-run.mdx";
import UsefulNextSteps from "/snippets/useful-next-steps.mdx";
import TriggerTaskNextjs from "/snippets/trigger-tasks-nextjs.mdx";
import NextjsTroubleshootingMissingApiKey from "/snippets/nextjs-missing-api-key.mdx";
import NextjsTroubleshootingButtonSyntax from "/snippets/nextjs-button-syntax.mdx";
import AddEnvironmentVariables from "/snippets/add-environment-variables.mdx";
import DeployingYourTask from "/snippets/deplopying-your-task.mdx";
import VercelDocsCards from "/snippets/vercel-docs-cards.mdx";

import RunDevAndNextConcurrently from "/snippets/run-dev-and-next-concurrently.mdx";

<Note>This guide can be followed for both App and Pages router as well as Server Actions.</Note>

<Prerequisites framework="Next.js" />

## Initial setup

<Steps>
  <CliInitStep />
  <CliDevStep />
  <CliRunTestStep />
  <CliViewRunStep />
</Steps>

<RunDevAndNextConcurrently />

## Set your secret key locally

Set your `TRIGGER_SECRET_KEY` environment variable in your `.env.local` file if using the Next.js App router or `.env` file if using Pages router. This key is used to authenticate with Trigger.dev, so you can trigger runs from your Next.js app. Visit the API Keys page in the dashboard and select the DEV secret key.

![How to find your secret key](/images/api-keys.png)

For more information on authenticating with Trigger.dev, see the [API keys page](/apikeys).

## Triggering your task in Next.js

Here are the steps to trigger your task in the Next.js App and Pages router and Server Actions. Alternatively, check out this repo for a [full working example](https://github.com/triggerdotdev/example-projects/tree/main/nextjs/server-actions/my-app) of a Next.js app with a Trigger.dev task triggered using a Server Action.

<Tabs>

  <Tab title="App Router">

  <Steps>

  <Step title="Create a Route Handler">

      Add a Route Handler by creating a `route.ts` file (or `route.js` file) in the `app/api` directory like this: `app/api/hello-world/route.ts`.

  </Step>

  <Step title="Add your task">

      Add this code to your `route.ts` file which imports your task along with `NextResponse` to handle the API route response:

      ```ts app/api/hello-world/route.ts
      // Next.js API route support: https://nextjs.org/docs/api-routes/introduction
      import type { helloWorldTask } from "@/trigger/example";
      import { tasks } from "@trigger.dev/sdk";
      import { NextResponse } from "next/server";

      //tasks.trigger also works with the edge runtime
      //export const runtime = "edge";

      export async function GET() {
        const handle = await tasks.trigger<typeof helloWorldTask>(
          "hello-world",
          "James"
        );

        return NextResponse.json(handle);
      }
      ```

    </Step>

    <Step title="Trigger your task">

    <TriggerTaskNextjs/>

    </Step>

    </Steps>

    </Tab>

    <Tab title="App Router (Server Actions)">

    <Steps>

    <Step title="Create an `actions.ts` file">

      Create an `actions.ts` file in the `app/api` directory and add this code which imports your `helloWorldTask()` task. Make sure to include `"use server";` at the top of the file.

      ```ts app/api/actions.ts
        "use server";

        import type { helloWorldTask } from "@/trigger/example";
        import { tasks } from "@trigger.dev/sdk";

        export async function myTask() {
          try {
            const handle = await tasks.trigger<typeof helloWorldTask>(
              "hello-world",
              "James"
            );

            return { handle };
          } catch (error) {
            console.error(error);
            return {
              error: "something went wrong",
            };
          }
        }
      ```

    </Step>

    <Step title="Create a button to trigger your task">

      For the purposes of this guide, we'll create a button with an `onClick` event that triggers your task. We'll add this to the `page.tsx` file so we can trigger the task by clicking the button. Make sure to import your task and include `"use client";` at the top of your file.

      ```ts app/page.tsx
      "use client";

      import { myTask } from "./actions";

      export default function Home() {
        return (
          <main className="flex min-h-screen flex-col items-center justify-center p-24">
            <button
              onClick={async () => {
                await myTask();
              }}
            >
              Trigger my task
            </button>
          </main>
        );
      }
      ```
    </Step>

    <Step title="Trigger your task">

    Run your Next.js app:

    <CodeGroup>

    ```bash npm
    npm run dev
    ```

    ```bash pnpm
    pnpm run dev
    ```

    ```bash yarn
    yarn dev
    ```

    </CodeGroup>

    Open your app in a browser, making sure the port number is the same as the one you're running your Next.js app on. For example, if you're running your Next.js app on port 3000, visit:

    ```bash
    http://localhost:3000
    ```

    Run the dev server from Step 2. of the [Initial Setup](/guides/frameworks/nextjs#initial-setup) section above if it's not already running:

    <CodeGroup>

    ```bash npm
    npx trigger.dev@latest dev
    ```

    ```bash pnpm
    pnpm dlx trigger.dev@latest dev
    ```

    ```bash yarn
    yarn dlx trigger.dev@latest dev
    ```

    </CodeGroup>

    Then click the button we created in your app to trigger the task. You should see the CLI log the task run with a link to view the logs.

    ![Trigger.dev CLI showing a successful run](/images/trigger-cli-run-success.png)

    Visit the [Trigger.dev dashboard](https://cloud.trigger.dev) to see your run.

    </Step>

    </Steps>

    </Tab>

    <Tab title="Pages Router">

    <Steps>

    <Step title="Create an API route">

      Create an API route in the `pages/api` directory. Then create a `hello-world .ts` (or `hello-world.js`) file for your task and copy this code example:

      ```ts pages/api/hello-world.ts
      // Next.js API route support: https://nextjs.org/docs/api-routes/introduction
      import { helloWorldTask } from "@/trigger/example";
      import { tasks } from "@trigger.dev/sdk";
      import type { NextApiRequest, NextApiResponse } from "next";

      export default async function handler(
        req: NextApiRequest,
        res: NextApiResponse<{ id: string }>
      ) {
        const handle = await tasks.trigger<typeof helloWorldTask>(
        "hello-world",
        "James"
        );

        res.status(200).json(handle);
      }
      ```
    </Step>

    <Step title="Trigger your task">

    <TriggerTaskNextjs/>

    </Step>

    </Steps>

    </Tab>

</Tabs>

## Automatically sync environment variables from your Vercel project (optional)

If you want to automatically sync environment variables from your Vercel project to Trigger.dev, you can add our `syncVercelEnvVars` build extension to your `trigger.config.ts` file.

<Note>
  You need to set the `VERCEL_ACCESS_TOKEN` and `VERCEL_PROJECT_ID` environment variables, or pass
  in the token and project ID as arguments to the `syncVercelEnvVars` build extension. If you're
  working with a team project, you'll also need to set `VERCEL_TEAM_ID`, which can be found in your
  team settings. You can find / generate the `VERCEL_ACCESS_TOKEN` in your Vercel
  [dashboard](https://vercel.com/account/settings/tokens). Make sure the scope of the token covers
  the project with the environment variables you want to sync.
</Note>

```ts trigger.config.ts
import { defineConfig } from "@trigger.dev/sdk";
import { syncVercelEnvVars } from "@trigger.dev/build/extensions/core";

export default defineConfig({
  project: "<project ref>",
  // Your other config settings...
  build: {
    extensions: [syncVercelEnvVars()],
  },
});
```

<Note>
  For more information, see our [Vercel sync environment
  variables](/guides/examples/vercel-sync-env-vars) guide.
</Note>

<AddEnvironmentVariables />

<DeployingYourTask />

## Troubleshooting & extra resources

### Revalidation from your Trigger.dev tasks

[Revalidation](https://vercel.com/docs/incremental-static-regeneration/quickstart#on-demand-revalidation) allows you to purge the cache for an ISR route. To revalidate an ISR route from a Trigger.dev task, you have to set up a handler for the `revalidate` event. This is an API route that you can add to your Next.js app.

This handler will run the `revalidatePath` function from Next.js, which purges the cache for the given path.

The handlers are slightly different for the App and Pages router:

#### Revalidation handler: App Router

If you are using the App router, create a new revalidation route at `app/api/revalidate/path/route.ts`:

```ts app/api/revalidate/path/route.ts
import { NextRequest, NextResponse } from "next/server";
import { revalidatePath } from "next/cache";

export async function POST(request: NextRequest) {
  try {
    const { path, type, secret } = await request.json();
    // Create a REVALIDATION_SECRET and set it in your environment variables
    if (secret !== process.env.REVALIDATION_SECRET) {
      return NextResponse.json({ message: "Invalid secret" }, { status: 401 });
    }

    if (!path) {
      return NextResponse.json({ message: "Path is required" }, { status: 400 });
    }

    revalidatePath(path, type);

    return NextResponse.json({ revalidated: true });
  } catch (err) {
    console.error("Error revalidating path:", err);
    return NextResponse.json({ message: "Error revalidating path" }, { status: 500 });
  }
}
```

#### Revalidation handler: Pages Router

If you are using the Pages router, create a new revalidation route at `pages/api/revalidate/path.ts`:

```ts pages/api/revalidate/path.ts
import type { NextApiRequest, NextApiResponse } from "next";

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
  try {
    if (req.method !== "POST") {
      return res.status(405).json({ message: "Method not allowed" });
    }

    const { path, secret } = req.body;

    if (secret !== process.env.REVALIDATION_SECRET) {
      return res.status(401).json({ message: "Invalid secret" });
    }

    if (!path) {
      return res.status(400).json({ message: "Path is required" });
    }

    await res.revalidate(path);

    return res.json({ revalidated: true });
  } catch (err) {
    console.error("Error revalidating path:", err);
    return res.status(500).json({ message: "Error revalidating path" });
  }
}
```

#### Revalidation task

This task takes a `path` as a payload and will revalidate the path you specify, using the handler you set up previously.

<Note>

To run this task locally you will need to set the `REVALIDATION_SECRET` environment variable in your `.env.local` file (or `.env` file if using Pages router).

To run this task in production, you will need to set the `REVALIDATION_SECRET` environment variable in Vercel, in your project settings, and also in your environment variables in the Trigger.dev dashboard.

</Note>

```ts trigger/revalidate-path.ts
import { logger, task } from "@trigger.dev/sdk";

const NEXTJS_APP_URL = process.env.NEXTJS_APP_URL; // e.g. "http://localhost:3000" or "https://my-nextjs-app.vercel.app"
const REVALIDATION_SECRET = process.env.REVALIDATION_SECRET; // Create a REVALIDATION_SECRET and set it in your environment variables

export const revalidatePath = task({
  id: "revalidate-path",
  run: async (payload: { path: string }) => {
    const { path } = payload;

    try {
      const response = await fetch(`${NEXTJS_APP_URL}/api/revalidate/path`, {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
        },
        body: JSON.stringify({
          path: `${NEXTJS_APP_URL}/${path}`,
          secret: REVALIDATION_SECRET,
        }),
      });

      if (response.ok) {
        logger.log("Path revalidation successful", { path });
        return { success: true };
      } else {
        logger.error("Path revalidation failed", {
          path,
          statusCode: response.status,
          statusText: response.statusText,
        });
        return {
          success: false,
          error: `Revalidation failed with status ${response.status}: ${response.statusText}`,
        };
      }
    } catch (error) {
      logger.error("Path revalidation encountered an error", {
        path,
        error: error instanceof Error ? error.message : String(error),
      });
      return {
        success: false,
        error: `Failed to revalidate path due to an unexpected error`,
      };
    }
  },
});
```

#### Testing the revalidation task

You can test your revalidation task in the Trigger.dev dashboard on the testing page, using the following payload.

```json
{
  "path": "<path-to-revalidate>" // e.g. "blog"
}
```

    <NextjsTroubleshootingMissingApiKey/>
    <NextjsTroubleshootingButtonSyntax/>

<VercelDocsCards />
<UsefulNextSteps />
